package org.example.mvc;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.annotation.CheckSame;
import org.example.mvc.obj.Entity;
import org.example.util.ReflectUtil;
import org.example.util.collection.MapUtil;
import org.example.util.collection.Pair;
import org.example.util.sql.WrapperUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;


/**
 * TODO 写个注释呗
 *
 * @author Maple
 * @date 2024-06-13
 */
@RequiredArgsConstructor
public abstract class BaseEntityHandler<E extends Entity, M extends BaseMapper<E>> {


    protected static final Map<Class<?>, List<Pair<String, CheckSame>>> CHECK_SAME_COLUM = new ConcurrentHashMap<>();
    
    protected final M mapper;

    public int insert (E entry) {

        if(entry == null) return 0;

        Date date = new Date();
        entry.setCreatedTime(date);
        entry.setModifiedTime(date);
        
        checkDouble(entry);
        return mapper.insert(entry);
    }

    public List<E> queryAll () {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        return queryByWrapper(queryWrapper);
    }

    public List<E> queryByEntry (E entry) {
        QueryWrapper<E> entryQueryWrapper = WrapperUtil.getEntryQueryWrapper(entry);
        return queryByWrapper(entryQueryWrapper);
    }

    public List<E> queryByLike (E entry) {
        return queryByLike(entry, null);
    }

    public List<E> queryByLike (E entry, List<String> selectColum) {
        QueryWrapper<E> likeQueryWrapper = WrapperUtil.getLikeQueryWrapper(entry, selectColum);
        return queryByWrapper(likeQueryWrapper);
    }

    public List<E> queryByWrapper (QueryWrapper<E> queryWrapper) {
        return mapper.selectList(queryWrapper);
    }

    public List<E> queryByMap (Map<String, ?> map) {
        return queryByMap(map, null);
    }

    public List<E> queryByColum (String colum, Object value) {
        return queryByMap(MapUtil.getMap(colum, value));
    }

    public List<E> queryColumnsInList(String colum, List<?> value) {
        QueryWrapper<E> inQueryWrapper = WrapperUtil.getInQueryWrapper(colum, value);
        return queryByWrapper(inQueryWrapper);
    }

    public E queryByColumOne (String colum, Object value) {
        return queryByMapOne(MapUtil.getMap(colum, value));
    }

    public List<E> queryByMap (Map<String, ?> map, List<String> selectColum) {
        QueryWrapper<E> mapQueryWrapper = WrapperUtil.getMapQueryWrapper(map, selectColum);
        return queryByWrapper(mapQueryWrapper);
    }

    public E queryByMapOne (Map<String, ?> map) {
        List<E> entryList = queryByMap(map);
        if(null == entryList || entryList.isEmpty()) return null;
        return entryList.get(entryList.size() - 1);
    }

    public List<E> queryDataListByIds(List<Long> ids) {
        return mapper.selectBatchIds(ids);
    }

    public E queryDataById (Long id) {
        return mapper.selectById(id);
    }

    public int update (E entry) {
        checkDouble(entry);
        Date date = new Date();
        entry.setModifiedTime(date);
        
        // 没有id的话，切面注解里会自动报错的
        return mapper.updateById(entry);
    }


    public int updateByWrapper (UpdateWrapper<E> updateWrapper) {
        return mapper.update(updateWrapper);
    }

    public int updateByWrapper (Consumer<UpdateWrapper<E>> consumer) {
        UpdateWrapper<E> wrapper = new UpdateWrapper<>();
        consumer.accept(wrapper);
        return mapper.update(wrapper);
    }

    public int deleteById (Long id) {
        return mapper.deleteById(id);
    }

    public E selectMax(String colum) {
        colum = WrapperUtil.toULC(colum);
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(colum);
        queryWrapper.orderByDesc(colum);
        queryWrapper.last("LIMIT 1");
        return mapper.selectOne(queryWrapper);
    }

    @SuppressWarnings("unchecked")
    public Class<E> getEntryClass () {
        return (Class<E>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
    }

    private List<Pair<String, CheckSame>> getCheckSameColum(Class<?> aClass) {
        if(null == aClass) return null;
        List<Pair<String, CheckSame>> res = CHECK_SAME_COLUM.get(aClass);
        if(null == res) {
            return getCheckSameColum(aClass.getSuperclass());
        }
        return res;
    }

    protected void checkDouble(E entry) {

        List<Pair<String, CheckSame>> columns = getCheckSameColum(entry.getClass());
        if(columns == null || columns.isEmpty()) return;

        Long id = ReflectUtil.getId(entry);

        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne(WrapperUtil.toULC("id"), null == id ? -1 : id);

        if (CollectionUtil.isNotEmpty(columns)) {
            queryWrapper.and(w -> {
                for (Pair<String, CheckSame> info : columns) {
                    Object value = ReflectUtil.getFieldValue(info.getLeft(), entry);
                    if (null == value) {
                        w.eq("1",2);
                    }

                    String[] allowSameValue = info.getRight().allowSameValue();
                    if (ArrayUtils.contains(allowSameValue, value)){
                        w.eq("1",2);
                    }

                    String[] otherColumns = info.getRight().otherColum();
                    if (ArrayUtils.isEmpty(otherColumns) || StringUtils.isEmpty(otherColumns[0])) {
                        w.eq( WrapperUtil.toULC(info.getLeft()), value).or();
                    } else {
                        w.and(w2 -> {
                            w2.eq( WrapperUtil.toULC(info.getLeft()), value);
                            for (String otherColumn : otherColumns) {
                                Object otherValue = ReflectUtil.getFieldValue(otherColumn, entry);
                                if (null != otherValue) {
                                    w2.eq( WrapperUtil.toULC(otherColumn), otherValue);
                                }
                            }
                        }).or();
                    }
                }
            });
        }

        List<String> errMsg = new LinkedList<>();

        columns.forEach(c -> errMsg.add(c.getRight().value()));

        List<E> entryList = mapper.selectList(queryWrapper);
        for (E en : entryList) {
            if(!Objects.equals(ReflectUtil.getId(en), id)) {
                throw new RuntimeException(String.join(" 或 ", errMsg));
            }
        }
    }

    {
        // 判断是否有需要检查字段重名

        Class<E> entryClass = getEntryClass();

        Field[] fields = entryClass.getDeclaredFields();

        for (Field field : fields) {
            if(field.isAnnotationPresent(CheckSame.class)){
                CheckSame annotation = field.getAnnotation(CheckSame.class);
                CHECK_SAME_COLUM.computeIfAbsent(entryClass, k -> new ArrayList<>()).add(new Pair<>(field.getName(), annotation));
            }
        }

    }
}
